Hello World

# Hello World

node.js中文文档 (opens new window)

[TOC]

# 一、认识Node.js

  • Node.js采用C++语言编写而成,是一个JavaScript的运行环境。
  • Node.js采用了谷歌浏览器的V8引擎,性能很好,同时还提供了很多系统级的API,如文件操作、网络编程等。
  • Node.js采用事件驱动、异步编程,为网络服务而生。
    • 充分利用了系统资源,执行代码无须阻塞等待某种操作,有限的资源可以用于其他任务。
  • Node.js采用单进程、单线程模式。
  • Node.js支持的编程语言是JavaScript。
    • JavaScript的匿名函数和闭包特性非常适合事件驱动、异步编程。
    • JavaScript在动态语言中性能较好。

# 二、第一个服务端应用

# 2.1 Hello World

短短几行代码,就可以构建服务器。

// $ node app.js
// 导入node自带的http模块
const http = require('http');

// 默认就是127.0.0.1
const hostname = '127.0.0.1';
const port = 3000;

// 开启http服务
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  // 在客户端写些东西
  res.write('write something\n')
  // 将内容返回到客户端,显式地结束请求
  res.end('Hello World\n');
});

// 监听本地3000端口
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 2.2 一个规范的接口

  • 接口名称:要体现出大致的使用场景,例如增删改查的动作。
  • 接口返回:要有规范化的标识,如成功与否、错误内容等。

# 2.3 获取命令行传来的参数

const arguments = process.argv.splice(2)
1
  • process.argv[0] : 返回启动Node.js进程的可执行文件所在的绝对路径。

  • process.argv[1] : 为当前执行的JavaScript文件路径。

  • process.argv.splice(2) : 移除前两者后,剩余的元素为其他命令行参数(也就是我们自定义部分)。

# 三、调试

# 3.1 Debugger

# 3.2 Node InSpector

可以像调试浏览器中的JavaScript代码一样调试Node中的JavaScript代码。

$ npm install -g node-inspector
1

# 四、文件系统

# 4.1 初识文件系统

  • fs模块主要用于文件的读写、移动、复制、删除、重命名等操作。
    • 同步:rename;异步:renameSync。
const fs = require('fs');
// 异步写入文件(path, data, cb),默认覆盖旧文件内容
fs.writeFile('./a.txt', 'write something', (err) => {
    err&&console.log(err);
})

// 异步读取文件
fs.readFile('./a.txt', (err,doc) => {
	if(err) {
        return console.error(err);
    }
    // 不加toString,则输出Buffer对象
    // <Buffer 77 72 69 74 65 20 73 6f 6d 65 74 68 69 6e 67>
    console.log(doc.toString());
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 4.2 req.url

  • 得到/开头的url
const http=require('http');
const fs=require('fs');

let server=http.createServer(function (req, res){
    // www文件夹存放写给浏览器的文件
    fs.readFile(`www${req.url}`, (err, buffer)=>{
        if(err){
            // 写给机器的
            res.writeHeader(404);
            // 写给人的
            res.write('Not Found');
            res.end();
        }else{
            res.write(buffer);
            res.end();
        }
    });
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 4.3 文件路径

var path = required('path')

console.log(__dirname)	// 总是返回被执行的js所在文件夹的绝对路径
console.log(__filename)	// 总是返回被执行的js的绝对路径
console.log(process.cwd())	// 总是返回运行 node 命令时所在的文件夹的绝对路径
console.log(path.resolve('./'))	// 总是返回运行 node 命令时所在的文件夹的绝对路径
1
2
3
4
5
6

# 4.3.1 path API

  • path.dirname(): 返回 path 的目录名。

  • path.join():所有给定的 path 片段连接到一起,然后规范化生成的路径。

  • path.resolve():方法会将路径或路径片段的序列解析为绝对路径,解析为相对于当前目录的绝对路径,相当于cd命令。 resolve把当成根目录。

# 五、接收浏览器的数据

# 5.1 表单GET提交

<!-- form_get.html -->
<form action="http://localhost:8080/aaa" method="get">
    用户:<input type="text" name="username" /><br>
    密码:<input type="password" name="password" /><br>
    <input type="submit" value="提交" />
</form>

<!-- 提交后 -> http://localhost:8080/aaa?username=123&password=aaa -->
1
2
3
4
5
6
7
8
// server_get1.js
const http=require('http');
const querystring=require('querystring');

let server=http.createServer(function (req, res){
    // 在?处切一刀
    let [url, query]=req.url.split('?');
    // 解析url参数
    let get=querystring.parse(query);

    console.log(url, get);
	// /aaa [Object: null prototype] { username: '123', password: 'aaa' }
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// server_get2.js
const http=require('http');
const url=require('url');

let server=http.createServer(function (req, res){
    // 加true是为将query由字符串转换成对象形式
    let {pathname, query}=url.parse(req.url, true);

    console.log(pathname, query);
    // 无true时:/aaa username=123&password=aaa
    // /aaa [Object: null prototype] { username: '123', password: 'aaa' }
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5.2 表单POST提交

# 5.2.1 对普通数据的处理

// server_post.js
const http=require('http');
const querystring=require('querystring');

let server=http.createServer(function (req, res){
    let arr=[];
    req.on('data', buffer=>{
        arr.push(buffer);
    });
    // 遇到end表示传完了
    req.on('end', ()=>{
        let buffer=Buffer.concat(arr);
        // 一般情况下不可以toString
        // 比如传输的数据是个视频而不是纯粹的字符串
        let post=querystring.parse(buffer.toString());

        console.log(post);
        // [Object: null prototype] { username: '123', password: 'aaa' }
    });
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 5.2.2 对文件数据的处理

自己写原生的话,要对http协议的规范要很清楚,还要懂得对buffer二进制数据的处理。

这里可以借助一个multiparty包(要自己先安装)。

// file_post.html
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
    用户:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="file" name="f1">
    <input type="submit" value="提交">
</form>
1
2
3
4
5
6
7
// server.js
const http=require('http');
const multiparty=require('multiparty');

http.createServer((req, res)=>{
    let form=new multiparty.Form({
        uploadDir: './upload'
    });

    form.parse(req);

    form.on('field', (name, value)=>{
        console.log('字段:', name, value);
    });
    form.on('file', (name, file)=>{
        console.log('文件:', name, file);
    });

    form.on('close', ()=>{
        console.log('表单解析完成');
    });
}).listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 5.3 GET和POST的整合

// server_total.js
const http=require('http');
const url=require('url');
const querystring=require('querystring');
const fs=require('fs');

http.createServer((req, res)=>{
  let path='', get={}, post={};

  if(req.method=='GET'){
    let {pathname, query}=url.parse(req.url, true);

    path=pathname;
    get=query;
    complete();
  }else if(req.method=='POST'){
    path=req.url;

    let arr=[];
    req.on('data', buffer=>{
      arr.push(buffer);
    });
    req.on('end', ()=>{
      let buffer=Buffer.concat(arr);

      post=querystring.parse(buffer.toString());
      complete();
    });
  }

  function complete(){
    console.log(path, get, post);
  }
}).listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 5.3.1 应用-注册登录

<head>
    <meta charset="utf-8">
    <title></title>
    <script src="jquery.js" charset="utf-8"></script>
    <script>
        $(function (){
            $('#btn1').click(()=>{
                $.ajax({
                    url: '/reg',
                    data: {
                        username: $('#user').val(),
                        password: $('#pass').val()
                    },
                    dataType: 'json'
                }).then(json=>{
                    if(json.error){
                        alert(json.msg);
                    }else{
                        alert('注册成功');
                    }
                }, err=>{
                    alert('注册失败,请刷新重试');
                });
            });

            $('#btn2').click(()=>{
                $.ajax({
                    url: '/login',
                    data: {
                        username: $('#user').val(),
                        password: $('#pass').val()
                    },
                    dataType: 'json'
                }).then(json=>{
                    if(json.error){
                        alert(json.msg);
                    }else{
                        alert('登录成功');
                    }
                }, err=>{
                    alert('登录失败,请刷新重试');
                });
            });
        });
    </script>
</head>
<body>
    用户:<input type="text" id="user" /><br>
    密码:<input type="password" id="pass" /><br>
    <input type="button" value="注册" id="btn1">
    <input type="button" value="登录" id="btn2">
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// server.js
const http=require('http');
const url=require('url');
const querystring=require('querystring');
const fs=require('fs');

let users={};

http.createServer((req, res)=>{
    let path='', get={}, post={};

    if(req.method=='GET'){
        //...
    }else if(req.method=='POST'){
        //...
    }

    function complete(){
        if(path=='/register'){
            let {username, password}=get;

            if(users[username]){
                res.write(JSON.stringify({error: 1, msg: '此用户名已存在'}));
                res.end();
            }else{
                users[username]=password;

                res.write(JSON.stringify({error: 0, msg: ''}));
                res.end();
            }
        }else if(path=='/login'){
            let {username, password}=get;

            if(!users[username]){
                res.write(JSON.stringify({error: 1, msg: '找不到此用户'}));
                res.end();
            }else if(users[username]!=password){
                res.write(JSON.stringify({error: 1, msg: '密码不对'}));
                res.end();
            }else{
                res.write(JSON.stringify({error: 0, msg: ''}));
                res.end();
            }
        }else{
            fs.readFile(`www${path}`, (err, buffer)=>{
                if(err){
                    res.writeHeader(404);
                    res.write('Not Found');
                    res.end();
                }else{
                    res.write(buffer);
                    res.end();
                }
            });
        }
    }
}).listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# 六、其他

# 6.1 零碎知识

# 6.1.1 url模块

  • url.parse:可以将一个url的字符串解析并返回一个url的对象。
const url = require('url')
url.parse("http://baidu.com:8080/test/h?query=js#node")

Url {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'baidu.com:8080',
  port: '8080',
  hostname: 'baidu.com',
  hash: '#node',
  search: '?query=js',
  query: 'query=js',
  pathname: '/test/h',
  path: '/test/h?query=js',
  href: 'http://baidu.com:8080/test/h?query=js#node'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • url.format:将传入的url对象编程一个url字符串并返回。
url.format({
  protocol: 'https',
  hostname: 'example.com',
  pathname: '/some/path',
  query: {
    page: 1,
    format: 'json'
  }
});

// => 'https://example.com/some/path?page=1&format=json'
1
2
3
4
5
6
7
8
9
10
11

# 6.1.2